home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / scope / 101-125 / scopedisk106 / bbs-index / src / command.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-19  |  36.4 KB  |  1,003 lines

  1. /*
  2.  * COMMANDS.C
  3.  *
  4.  * This module contains all the command parsing stuff used to parse the
  5.  * commands in the script file.
  6.  *
  7.  */
  8.  
  9. #ifndef LATTICE_50
  10. #include "system.h"
  11. #endif
  12.  
  13. #include "bbsindex.h"
  14.  
  15. /*
  16.  *             The list of command names and associated functions
  17.  */
  18.  
  19. void com_append(), com_close(), com_config(), com_database(), com_echo(),
  20.         com_exec(), com_format(), com_list(), com_macro(), com_msg(),
  21.         com_open(), com_reset(), com_scan(), com_trace(), com_ignore();
  22.  
  23. struct {
  24.        char *name;
  25.        void (*proc)();
  26. } com[] = {
  27.  
  28.        {       "APPEND",               com_append              },
  29.        {       "CHECKFILES",   com_checkfiles  },
  30.        {       "CLOSE",                com_close               },
  31.        {       "CONFIG",               com_config              },
  32.        {       "DATABASE",             com_database    },
  33.        {       "ECHO",                 com_echo                },
  34.        {       "EXEC",                 com_exec                },
  35.        {       "FOREIGN",              com_foreign             },
  36.        {       "FORMAT",               com_format              },
  37.        {       "IGNORE",               com_ignore              },
  38.        {       "LIST",                 com_list                },
  39.        {       "MACRO",                com_macro               },
  40.        {       "MSG",                  com_msg                 },
  41.        {       "NOREQUEST",    com_norequest   },
  42.        {       "OPEN",                 com_open                },
  43.        {       "RESET",                com_reset               },
  44.        {       "SCAN",                 com_scan                },
  45.        {       "SELECT",               com_select              },
  46.        {       "SORT",                 com_sort                },
  47.        {       "TRACE",                com_trace               },
  48.        {       NULL,                   NULL                    } 
  49. };
  50.  
  51. struct constval {
  52.        struct constval *next;
  53.        char *name;
  54.        char *value;
  55. };
  56. typedef struct constval CONST;
  57.  
  58. CONST *firstconst = NULL;              /* Pointer to first constant on list    */
  59.  
  60. static char line[MAXCOM];      /* Temporary line buffer */
  61.  
  62. /*
  63.  *             scripterror()
  64.  *             -------------
  65.  *             Prints an error message for the current script command; the filename
  66.  *             and linenumber are automatically output, followed by the user
  67.  *             specified message. The message should be terminated with a NL,
  68.  *             unless the caller intends outputting any more info afterwards.
  69.  */
  70. void scripterror(s)
  71. char *s;
  72. {
  73.        print3("===> ", scriptname, " (");
  74.        print3(itoa(linenum), "): ",s);
  75. }
  76.  
  77. /*
  78.  *             addconst()
  79.  *             --------
  80.  *             Creates a new constant entry, and links it into the list of 
  81.  *             existing constant definitions. The name of the constant is
  82.  *             initialised appropriately. A pointer to the constant is returned.
  83.  */
  84. CONST *addconst(name)
  85. char *name;
  86. {
  87.        CONST *tv;
  88.  
  89.        tv                      = mymalloc(sizeof(CONST));
  90.        tv->next        = firstconst;
  91.        firstconst      = tv;
  92.        tv->name        = mymalloc(strlen(name)+1);
  93.        strcpy(tv->name, name);
  94.        return (tv);
  95. }
  96.  
  97. /*
  98.  *             findconst()
  99.  *             ---------
  100.  *             Searches constant table for specified constant, and returns a
  101.  *             pointer to it if found, or NULL if not found.
  102.  */
  103. CONST *findconst(name)
  104. char *name;
  105. {
  106.        CONST *tv;
  107.  
  108.        for (tv = firstconst; tv && stricmp(name, tv->name); tv = tv->next)
  109.                ;
  110.        return (tv);
  111. }
  112.  
  113.  
  114. /*
  115.  *             readcommand()
  116.  *             -------------
  117.  *             This function reads a command from the script buffer into a
  118.  *             specified command buffer. The following modifications are made
  119.  *             to the original script text:
  120.  *
  121.  *             - Anything after a # to an end of line is ignored
  122.  *             - Any line ending with \ is continued on the next line
  123.  *             - Everything outside quotes is capitalised.
  124.  *             - Any white space outside quotes gets reduced to a single space
  125.  *             - White space surrounding command lines is removed.
  126.  *             - Any commas on the line are removed, and replaced by spaces
  127.  *
  128.  *             The script command is terminated by either a semicolon or a newline.
  129.  *             The command buffer is null-terminated on return. When the end of the
  130.  *             script is reached, a 0 is returned. The maximum size of the command
  131.  *             line is specified in max - if this is exceeded, then an error message
  132.  *             is generated. Normally, the length of the command line read in is
  133.  *             returned.
  134.  *
  135.  *             For the technically minded, a mini state machine is setup, to handle
  136.  *             the different requirements.
  137.  */
  138.  
  139. #define STATE_START            1       /* Skip over space at start of command                  */
  140. #define STATE_SPACE            2       /* Replace multiple white space by single space */
  141. #define STATE_COPY             3       /* Copy normal text, capitalising                               */
  142. #define STATE_IGNORE   4       /* If next char is newline, then skip it                */
  143. #define STATE_QUOTE            5       /* Copy text up until next quote                                */
  144. #define STATE_IGQUOTE  6       /* Like STATE_IGNORE, but between quotes                */
  145. #define STATE_COMMENT  7       /* Ignore everything until the next newline             */
  146. #define STATE_CONST_ST 8       /* Starting to expand a constant $(..)                  */
  147. #define STATE_CONST_CP  9      /* Copying constant name, and expanding it              */
  148.  
  149. /*
  150.  *             Retrieve next character from script buffer, updating line counter
  151.  *             and checking for end of buffer.
  152.  */
  153. #define nextchar(ch)                                                                   \
  154.        (       (ch) = script[scriptpos++],                                             \
  155.                ((ch) == CHAR_NL ? linenum++ : 0),                              \
  156.                ((scriptpos > scriptsize) ? (loop = 0) : 0)     \
  157.        )
  158.  
  159. /*
  160.  *             Add character to command line, checking for end of buffer and
  161.  *             aborting if the buffer is overrun.
  162.  */
  163. #define addchar(ch)                                                                            \
  164.        (       buf[pos++] = (ch),                                                              \
  165.                ((pos > max) ? (scripterror("line too long\n"), Cleanup(10)) : 0)       \
  166.        )
  167.  
  168. int readcommand(buf,max)
  169. char *buf;
  170. int max;
  171. {
  172.        int pos = 0;
  173.        int state = STATE_START;
  174.        int laststate;
  175.        int loop = 1;
  176.        int ch;
  177.        CONST *var;
  178.        char varname[MAXCONST], *p;
  179.        char openquote;
  180.        int varpos;
  181.  
  182.        if (scriptpos >= scriptsize)
  183.                return 0;
  184.  
  185.        nextchar(ch);
  186.        while (loop) {
  187.                switch (state) {
  188.  
  189.                        case STATE_START:
  190.                                switch (ch) {
  191.  
  192.                                        case CHAR_SPACE:
  193.                                        case CHAR_TAB:
  194.                                        case CHAR_SEMI:
  195.                                        case CHAR_NL:
  196.                                        case CHAR_COMMA:
  197.                                                nextchar(ch);
  198.                                                break;
  199.  
  200.                                        case CHAR_HASH:
  201.                                                state = STATE_COMMENT;
  202.                                                break;
  203.  
  204.                                        default:
  205.                                                state = STATE_COPY;
  206.                                                break;
  207.                                }
  208.                                break;
  209.  
  210.                        case STATE_SPACE:
  211.                                switch (ch) {
  212.  
  213.                                        case CHAR_SPACE:
  214.                                        case CHAR_TAB:
  215.                                        case CHAR_COMMA:
  216.                                                nextchar(ch);
  217.                                                break;
  218.  
  219.                                        case CHAR_NL:
  220.                                                linenum--;
  221.                                                /* Drop through */
  222.                                        case CHAR_SEMI:
  223.                                                scriptpos--;
  224.                                                loop = 0;
  225.                                                break;
  226.  
  227.                                        default:
  228.                                                addchar(CHAR_SPACE);
  229.                                                state = STATE_COPY;
  230.                                                break;
  231.                                }
  232.                                break;
  233.  
  234.                        case STATE_COPY:
  235.                                switch (ch) {
  236.  
  237.                                        /*
  238.                                         * If we get a # after a command, then we immediately
  239.                                         * stop, so that the comment will get eaten the NEXT
  240.                                         * time we call readcommand(). If we went into
  241.                                         * STATE_COMMENT, then the following command would end
  242.                                         * up getting read into the buffer as well.
  243.                                         *
  244.                                         */
  245.                                        case CHAR_HASH:
  246.                                                scriptpos--;
  247.                                                loop = 0;
  248.                                                break;
  249.  
  250.                                        case CHAR_QUOTE:
  251.                                        case CHAR_QUOTES:
  252.                                                addchar(ch);
  253.                                                openquote = ch;
  254.                                                state = STATE_QUOTE;
  255.                                                nextchar(ch);
  256.                                                break;
  257.  
  258.                                        case CHAR_DOLLAR:
  259.                                                laststate = state;      /* Save return state */
  260.                                                state = STATE_CONST_ST;
  261.                                                nextchar(ch);
  262.                                                break;
  263.  
  264.                                        case CHAR_NL:
  265.                                                linenum--;
  266.                                                /* Drop through */
  267.  
  268.                                        case CHAR_SEMI:
  269.                                                loop = 0;
  270.                                                scriptpos--;
  271.                                                break;
  272.  
  273.                                        case CHAR_ESC:
  274.                                                nextchar(ch);
  275.                                                state = STATE_IGNORE;
  276.                                                break;
  277.  
  278.                                        case CHAR_SPACE:
  279.                                        case CHAR_TAB:
  280.                                        case CHAR_COMMA:
  281.                                                state = STATE_SPACE;
  282.                                                break;
  283.  
  284.                                        default:
  285.                                                addchar(toupper(ch));
  286.                                                nextchar(ch);
  287.                                                break;
  288.                                }
  289.                                break;
  290.  
  291.                        case STATE_IGNORE:
  292.                                if (ch == CHAR_NL)
  293.                                        ch = CHAR_SPACE;
  294.                                addchar(ch);
  295.                                nextchar(ch);
  296.                                state = STATE_COPY;
  297.                                break;
  298.  
  299.                        case STATE_QUOTE:
  300.                                switch (ch) {
  301.  
  302.                                        case CHAR_NL:
  303.                                                scriptpos--;
  304.                                                linenum--;
  305.                                                loop = 0;
  306.                                                break;
  307.  
  308.                                        case CHAR_ESC:
  309.                                                state = STATE_IGQUOTE;
  310.                                                nextchar(ch);
  311.                                                break;
  312.  
  313.                                        case CHAR_DOLLAR:
  314.                                                laststate = state;
  315.                                                state = STATE_CONST_ST;
  316.                                                nextchar(ch);
  317.                                                break;
  318.  
  319.                                        case CHAR_QUOTE:
  320.                                        case CHAR_QUOTES:
  321.                                                if (openquote == ch)
  322.                                                        state = STATE_COPY;
  323.                                                /* Deliberate fall through to next switch */
  324.  
  325.                                        default:
  326.                                                addchar(ch);
  327.                                                nextchar(ch);
  328.                                                break;
  329.                                }
  330.                                break;
  331.  
  332.                        case STATE_IGQUOTE:
  333.                                if (ch != CHAR_NL) {
  334.                                        addchar(CHAR_ESC);
  335.                                        addchar(ch);
  336.                                }
  337.                                nextchar(ch);
  338.                                state = STATE_QUOTE;
  339.                                break;
  340.  
  341.                        case STATE_COMMENT:
  342.                                if (ch == CHAR_NL)
  343.                                        state = STATE_START;
  344.                                nextchar(ch);
  345.                                break;
  346.  
  347.                        case STATE_CONST_ST:
  348.                                if (ch != '(') { /* If not a constant usage, copy unchanged */
  349.                                        addchar(CHAR_DOLLAR);
  350.                                        state = laststate;
  351.                                        break;
  352.                                }
  353.                                varpos = 0;
  354.                                nextchar(ch);
  355.                                state = STATE_CONST_CP;
  356.                                break;
  357.  
  358.                        case STATE_CONST_CP:
  359.                                if (ch == ')') {
  360.                                        /*
  361.                                         *              Variable name has been fully entered, so now
  362.                                         *              expand it.
  363.                                         */
  364.                                        varname[varpos] = CHAR_NULL;
  365.                                        var = findconst(varname);
  366.                                        if (!var) {
  367.                                                scripterror("unknown constant ");
  368.                                                print2(varname, ".\n");
  369.                                                Cleanup(10);
  370.                                        }
  371.                                        for (p = var->value; *p; p++)
  372.                                                addchar(*p);
  373.                                        state = laststate;
  374.                                } else {
  375.                                        /*
  376.                                         *              Else still gathering constant name, so keep
  377.                                         *              copying into array.
  378.                                         */
  379.                                        if (varpos >= MAXCONST) {
  380.                                                scripterror("constant name too long.\n");
  381.                                                Cleanup(10);
  382.                                        }
  383.                                        varname[varpos++] = ch;
  384.                                }
  385.                                nextchar(ch);
  386.                                break;
  387.                }
  388.        }
  389.        addchar(CHAR_NULL);     /* Null terminate command string */
  390.        compos = 0;
  391.        comlen = strlen(buf);
  392.        return (comlen);
  393. }
  394.  
  395. /*
  396.  *             findmacro()
  397.  *             -----------
  398.  *             Searches the macro table for the specified macro. If found, returns
  399.  *             pointer to the macro definition, else returns -1.
  400.  */
  401. int findmacro(name)
  402. char *name;
  403. {
  404.        int i;
  405.  
  406.        for (i = 0; i < nummacros; i++)
  407.                if (!strcmp(macros[i]->name, name))
  408.                        return (i);
  409.        return (-1);
  410. }
  411.  
  412. /*
  413.  *             getstring()
  414.  *             -----------
  415.  *             This function scans the command buffer starting at position
  416.  *             'compos', and returns a pointer to the next identifier/string in
  417.  *             the buffer. The string is null-terminated, and if it was enclosed
  418.  *             in quotes, these are removed. compos is left pointing to just after
  419.  *             the string.
  420.  */
  421. char *getstring()
  422. {
  423.        char *s = combuf + compos;
  424.        char *p, openquote;
  425.  
  426.        if (compos > comlen) {
  427.                scripterror("missing parameter\n");
  428.                Cleanup(10);
  429.        }
  430.        if (*s == CHAR_SPACE)
  431.                s++;
  432.  
  433.        if (*s == CHAR_QUOTE || *s == CHAR_QUOTES) {
  434.                openquote = *s++;
  435.                for (p = s; *p && *p != openquote; p++) {
  436.                        if (*p == CHAR_ESC)
  437.                                p++;    /* Skip over possible escaped quotes */
  438.                }
  439.                *p = CHAR_NULL;
  440.                compos = (p - combuf) + 1;
  441.                return (s);
  442.        }
  443.  
  444.        for (p = s; *p && *p != CHAR_SPACE; p++)
  445.                ;
  446.        *p = CHAR_NULL;
  447.        compos = (p - combuf) + 1;
  448.        return (s);
  449. }
  450.  
  451.  
  452. /*
  453.  *             execline()
  454.  *             ----------
  455.  *             This function executes the current line in the command buffer,
  456.  *             handling macro expansion as necessary.
  457.  */
  458. void execline()
  459. {
  460.        char *cmd;
  461.        char *p, *s;
  462.        char *equals;
  463.        MACRO *curmac;
  464.        CONST *var;
  465.        int macronum;
  466.        char *ps[10];
  467.        PARAM *par;
  468.        int length;
  469.        int i;
  470.  
  471.        chkabort();
  472.        if (tracemode) {
  473.                if (nestlevel > 0) {
  474.                        int i;
  475.                        for (i = 0; i < nestlevel; i++)
  476.                                print("    ");
  477.                        print2(combuf, "\n");
  478.                } else {
  479.                        print3(itoa(linenum), ":", scriptname);
  480.                        print3(": ", combuf, "\n");
  481.                }
  482.        }
  483.        cmd = getstring();
  484.        for (i = 0; com[i].name && strcmp(cmd, com[i].name); i++)
  485.                ;
  486.        if (com[i].name) {
  487.                /*
  488.                 *              A valid command was found, so execute it
  489.                 */
  490.                com[i].proc();
  491.        } else if ((macronum = findmacro(cmd)) == -1) {
  492.                /*
  493.                 *              Not a macro -- is it a constant definition?
  494.                 */
  495.                if (compos < comlen) {
  496.                        /*
  497.                         *              There's a parameter after it; check is it an equals sign
  498.                         */
  499.                        equals = getstring();
  500.                        if (equals[0] != CHAR_EQUALS || equals[1] != CHAR_NULL) {
  501.                                /*
  502.                                 *              Wasn't an equals, so probably just a wrong command
  503.                                 *              with some superfluous parameters.
  504.                                 */
  505.                                scripterror("unknown command ");
  506.                                print2(cmd, "\n");
  507.                                Cleanup(10);
  508.                        }
  509.                        /*
  510.                         *              Okay, it's a constant definition. If it's already been
  511.                         *              defined, print a warning but carry on anyway.
  512.                         */
  513.                        var = findconst(cmd);
  514.                        if (var) {
  515.                                scripterror("constant ");
  516.                                print2(cmd, " redefined.\n");
  517.                        } else
  518.                                var = addconst(cmd);
  519.                        /*
  520.                         *              Now see was a value specified for the constant.
  521.                         *              If it was, copy it into constant, else just 
  522.                         *              setup a null definition.
  523.                         */
  524.                        if (compos < comlen) {
  525.                                /*
  526.                                 *              Copy user's definition
  527.                                 */
  528.                                cmd = getstring();
  529.                                if (strlen(cmd) >= MAXCONST) {
  530.                                        scripterror("constant name too long.\n");
  531.                                        Cleanup(10);
  532.                                }
  533.                                var->value = mymalloc(strlen(cmd)+1);
  534.                                strcpy(var->value, cmd);
  535.                        } else {
  536.                                /*
  537.                                 *              Setup null constant definition
  538.                                 */
  539.                                var->value = mymalloc(1);
  540.                                strcpy(var->value, "");
  541.                        }
  542.                } else {
  543.                        /*
  544.                         *              Not a command, not a macro, not a constant definition.
  545.                         *              Therefore, must be an error.
  546.                         */
  547.                        scripterror("unknown command ");
  548.                        print2(cmd, "\n");
  549.                        Cleanup(10);
  550.                }
  551.        } else {
  552.                /*
  553.                 *              It's a macro. Save a copy of the macro parameters into
  554.                 *              $0 to $9, and then execute each line of the macro definition,
  555.                 *              expanding parameter usages as necessary.
  556.                 */
  557.                curmac = macros[macronum];
  558.                length = 0;
  559.                if (nestlevel >= MAXNEST) {
  560.                        scripterror("macros can only be nested ");
  561.                        print2(MAXNEST, " deep\n");
  562.                        Cleanup(10);
  563.                }
  564.                par = SafeAllocMem(PARAMSIZE + comlen + 1);
  565.                params[nestlevel++] = par;
  566.                par->size = comlen + 1;
  567.  
  568.                /*
  569.                 *              Now initialise parameters $0 to $9
  570.                 */
  571.                ps[0] = par->params;
  572.                for (i = 1; i < 10; i++) {
  573.                        if (compos < comlen)
  574.                                /* Save parameter for later */
  575.                                ps[i] = par->params + (getstring() - combuf);
  576.                        else
  577.                                ps[i] = NULL;
  578.                }
  579.                memcpy(par->params, combuf, comlen+1);
  580.  
  581.                /*
  582.                 *              Now recursively call execline() with command buffer setup
  583.                 *              to be the current macro buffer line.
  584.                 */
  585.                length = 0;
  586.                s = curmac->text;
  587.                for (length = 0; length < curmac->size;
  588.                                                length += strlen(s) + 1, s = curmac->text + length) {
  589.                        /* Copy macro line into buffer, doing expansion */
  590.                        comlen = 0;
  591.                        for (p = s; *p; p++) {
  592.                                if (*p == '$') {
  593.                                        p++;
  594.                                        if (!*p) {
  595.                                                scripterror("'$' must be followed by something\n");
  596.                                                Cleanup(10);
  597.                                        }
  598.                                        if (isdigit(*p)) {
  599.                                                int num = *p - '0';
  600.  
  601.                                                if (ps[num]) {
  602.                                                        /* Copy macro definition */
  603.                                                        if ((strlen(ps[num]) + comlen) >= MAXCOM - 10)
  604.                                                                goto endcommand;        /* Eek! my first C Goto! */
  605.                                                        strcpy(combuf+comlen, ps[num]);
  606.                                                        comlen += strlen(ps[num]);
  607.                                                }
  608.                                                continue;
  609.  
  610.                                        }
  611.                                }
  612.                                combuf[comlen++] = *p;
  613.                        }
  614. endcommand:
  615.                        combuf[comlen] = CHAR_NULL;
  616.                        compos = 0;
  617.                        execline();
  618.                }
  619.                /*
  620.                 *              Now free memory allocated for macro parameters
  621.                 */
  622.                FreeMem(par, PARAMSIZE + par->size);
  623.                nestlevel--;
  624.        }
  625. }
  626.  
  627.  
  628. /*
  629.  *             execscript()
  630.  *             ------------
  631.  *             This function goes through all the commands in the script file,
  632.  *             executing them one by one. Each command is parsed, and then
  633.  *             the appropriate function called. Any errors that occur cause the
  634.  *             script to be aborted, so if this call returns, the script was
  635.  *             successfully executed.
  636.  */
  637. void execscript()
  638. {
  639.        while (readcommand(combuf, MAXCOM))
  640.                execline();
  641. }
  642.  
  643. /*
  644.  *             ====> Now the actual commands follow <====
  645.  */
  646.  
  647. /*
  648.  *             com_append()
  649.  *             ------------
  650.  *             This file opens the specified file for appending. If there was
  651.  *             already a file open, it is closed.
  652.  */
  653.  
  654. void com_append()
  655. {
  656.        char *filename = getstring();
  657.        BPTR lock;
  658.  
  659.        com_close();
  660.  
  661.        if (lock = Lock(filename, ACCESS_READ)) {
  662.                UnLock(lock);
  663.                outfile = Open(filename, MODE_OLDFILE);
  664.        } else
  665.                outfile = Open(filename, MODE_NEWFILE);
  666.  
  667.        if (!outfile) {
  668.                scripterror("error appending to file ");
  669.                print2(filename, "\n");
  670.                Cleanup(10);
  671.        }
  672.        Seek(outfile, 0, OFFSET_END);
  673.        toscreen = IsInteractive(outfile);
  674. }
  675.  
  676. /*
  677.  *             com_open()
  678.  *             ----------
  679.  *             This command opens a file for output. The output of the ECHO,
  680.  *             FOREIGN and LIST commands goes to this file. If an output file
  681.  *             was already open, it is closed first.
  682.  */
  683. void com_open()
  684. {
  685.        char *filename = getstring();
  686.  
  687.        com_close();
  688.        outfile = Open(filename, MODE_NEWFILE);
  689.        if (!outfile) {
  690.                scripterror("error opening file ");
  691.                print2(filename, "\n");
  692.                Cleanup(10);
  693.        }
  694.        toscreen = IsInteractive(outfile);
  695. }
  696.  
  697. /*
  698.  *             com_close()
  699.  *             -----------
  700.  *             Closes the current output file, if any. All future output goes to
  701.  *             stdout.
  702.  */
  703. void com_close()
  704. {
  705.        if (outfile != Output()) {
  706.                flushout();
  707.                Close(outfile);
  708.                outfile = Output();
  709.                toscreen = IsInteractive(outfile);
  710.        }
  711. }
  712.  
  713. /*
  714.  *             com_config()
  715.  *             ------------
  716.  *             This command sets the name of the configuration file used by
  717.  *             CHECKFILES to get the names of the directories to scan from.
  718.  */
  719. void com_config()
  720. {
  721.        strcpy(configname, getstring());
  722. }
  723.  
  724. /*
  725.  *             com_database()
  726.  *             --------------
  727.  *             This command sets the name of the database file read in
  728.  *             which contains the details of all the file headers.
  729.  */
  730. void com_database()
  731. {
  732.        strcpy(databasename, getstring());
  733. }
  734.  
  735. /*
  736.  *             com_norequest()
  737.  *             ---------------
  738.  *             This command stops AmigaDos from putting up requesters when an
  739.  *             error occurs, or a volume isn't mounted. 
  740.  */
  741. void com_norequest()
  742. {
  743.        struct Process *me = (struct Process *)FindTask(0L);
  744.        me->pr_WindowPtr = (APTR)-1L;
  745. }
  746.  
  747. /*
  748.  *             com_echo()
  749.  *             ----------
  750.  *             This command echoes the specified string to the output file.
  751.  *             A number of meta characters may be present in the string to be
  752.  *             output. See the documentation for more info. If any other parameter
  753.  *             is present on the line after the string, no newline is added
  754.  *             to the output, otherwise a newline is added automatically.
  755.  *
  756.  */
  757. void com_echo()
  758. {
  759.        strcpy(line, getstring());
  760.        if (compos >= comlen)   /* Add NL if no second parameter */
  761.                strcat(line, "\n");
  762.        putstring(echoformat(out, MAXOUT, line));
  763. }
  764.  
  765. /*
  766.  *             com_exec()
  767.  *             ----------
  768.  *             This command executes the indicated AmigaDOS command. The output
  769.  *             from the command goes into the current output file, if any.
  770.  */
  771. void com_exec()
  772. {
  773.        flushout();
  774.        Execute(getstring(), NULL, outfile);
  775. }
  776.  
  777. /*
  778.  *             com_format()
  779.  *             ------------
  780.  *             This command sets up the format string used by LIST and FOREIGN.
  781.  *             For details of what the format may contain, see the documentation.
  782.  */
  783. void com_format()
  784. {
  785.        strcpy(formatstring, getstring());
  786.        if (compos >= comlen)                           /* No second parameter */
  787.                strcat(formatstring, "\n");
  788. }
  789.  
  790. /*
  791.  *             com_list()
  792.  *             ----------
  793.  *             This command is the biggy! It scans through the entire file
  794.  *             database, selecting records as specified with SELECT, and printing
  795.  *             them using the order specified with FORMAT. The order of printing
  796.  *             is as specified with SORT.
  797.  */
  798. void com_list()
  799. {
  800.        int i;
  801.  
  802.        CHECKDATABASE();
  803.        curbytes = 0;
  804.        curfiles = 0;
  805.     for (i = 0; i < numrecs; i++) {
  806.         if (ptrblock[i]->type == 0 && match(ptrblock[i], tree)) {
  807.             format(out, MAXOUT, formatstring, ptrblock[i], checkfiles);
  808.                        putstring(out);
  809.                        curfiles++;
  810.                        curbytes += ptrblock[i]->length;
  811.                }
  812.                chkabort();
  813.        }
  814.        totalbytes += curbytes;
  815.        totalfiles += curfiles;
  816. }
  817.  
  818. /*
  819.  *             com_msg()
  820.  *             ---------
  821.  *             This command is identical to the ECHO command, except that
  822.  *             the output goes to the screen (i.e. stderr) instead of the
  823.  *             current output file. It is intended for printing informational
  824.  *             messages to let the user know of the program's progress, while
  825.  *             processing a large script.
  826.  */
  827. void com_msg()
  828. {
  829.        strcpy(line, getstring());
  830.        if (compos >= comlen)   /* Add NL if no second parameter */
  831.                strcat(line, "\n");
  832.        print(echoformat(out, MAXOUT, line));
  833. }
  834.  
  835. /*
  836.  *             com_reset()
  837.  *             -----------
  838.  *             This command resets the running totals which are maintained, 
  839.  *             which give the total number of bytes and total number of files
  840.  *             output so far.
  841.  */
  842. void com_reset()
  843. {
  844.        totalfiles = 0;
  845.        totalbytes = 0;
  846. }
  847.  
  848. /*
  849.  *             com_scan()
  850.  *             ----------
  851.  *             This command is identical to com_list(), except that no output is
  852.  *             produced. It is provided to allow counters to be updated, without
  853.  *             producing output, so that for example, the total number of files
  854.  *             in a listing can be output before the files themselves.
  855.  */
  856.  
  857. void com_scan()
  858. {
  859.        int i;
  860.  
  861.        CHECKDATABASE();
  862.        curbytes = 0;
  863.        curfiles = 0;
  864.     for (i = 0; i < numrecs; i++) {
  865.         if (ptrblock[i]->type == 0 && match(ptrblock[i], tree)) {
  866.                        curfiles++;
  867.                        curbytes += ptrblock[i]->length;
  868.                }
  869.                chkabort();
  870.        }
  871.        totalbytes += curbytes;
  872.        totalfiles += curfiles;
  873. }
  874.  
  875. /*
  876.  *             com_macro()
  877.  *             -----------
  878.  *             This command lets you define a macro. The syntax is:
  879.  *
  880.  *                     MACRO name
  881.  *                               ..
  882.  *                             commands
  883.  *                               ..
  884.  *                     ENDM
  885.  *
  886.  *             The macro can contain $1 to $9, which will be substituted at
  887.  *             run time by the appropriate parameters that were passed when
  888.  *             the macro was invoked.
  889.  */
  890. void com_macro()
  891. {
  892.        static char macroname[MACROLEN];
  893.        int curline, curpos;    /* Saves current line # and position in script */
  894.        int length;                             /* Length of macro script                                          */
  895.        int macronum;
  896.        MACRO *curmac;
  897.        char *buf, *p;
  898.  
  899.        if (nummacros >= MAXMACRO) {
  900.                scripterror("maximum of ");
  901.                print2(itoa(MAXMACRO), " macros allowed\n");
  902.                Cleanup(10);
  903.        }
  904.  
  905.        curline = linenum;
  906.        curpos = scriptpos;
  907.        
  908.        p = getstring();
  909.        strncpy(macroname, p, MACROLEN-1);
  910.        macroname[MACROLEN-1] = CHAR_NULL;
  911.  
  912.        /*
  913.         *              First of all, find out how much space the macro occupies
  914.         */
  915.        length = 0;
  916.        while (readcommand(combuf, MAXCOM) && strcmp(combuf, "ENDM"))
  917.                length += comlen + 1;   /* The extra 1 is for the terminating \0 */
  918.  
  919.        /*
  920.         *              Now, restore linenumber and script position so we can read
  921.         *              in macro for real the second time.
  922.         */
  923.        linenum = curline;
  924.        if (scriptpos >= scriptsize) {
  925.                scripterror("missing ENDM in macro definition\n");
  926.                Cleanup(10);
  927.        }
  928.  
  929.        scriptpos = curpos;
  930.  
  931.        if ((macronum = findmacro(macroname)) == -1)            /* New macro */
  932.                macronum = nummacros++;
  933.        else
  934.                /* Macro redefinition, so release earlier definition */
  935.                FreeMem(macros[macronum], macros[macronum]->size + MACROSIZE);
  936.  
  937.        curmac = SafeAllocMem(MACROSIZE + length);
  938.        macros[macronum] = curmac;
  939.        strcpy(curmac->name, macroname);
  940.        curmac->size = length;
  941.        buf = curmac->text;
  942.  
  943.        /*
  944.         *              Initialised macro definition, now copy macro text from script
  945.         *              file into macro buffer.
  946.         */
  947.        while (readcommand(combuf, MAXCOM) && strcmp("ENDM", combuf)) {
  948.                strcpy(buf, combuf);
  949.                buf += comlen + 1;
  950.        }
  951. }
  952.  
  953. /*
  954.  *             com_trace()
  955.  *             -----------
  956.  *             Turns on or off trace mode. When trace mode is on, every line that
  957.  *             is executed is displayed first. This is handy when executing
  958.  *             macros, to see exactly what is happening. Note that the -t option
  959.  *             on the command line will enable tracing for the whole file,
  960.  *             but if TRACE ON or OFF is encountered, this overrides -t.
  961.  */
  962. void com_trace()
  963. {
  964.        char *opt = getstring();
  965.  
  966.        if (!strcmp(opt, "ON"))
  967.                tracemode = TRUE;
  968.        else if (!strcmp(opt, "OFF"))
  969.                tracemode = FALSE;
  970.        else {
  971.                scripterror("TRACE ON or TRACE OFF expected\n");
  972.                Cleanup(10);
  973.        }
  974. }
  975.  
  976. /*
  977.  *             com_ignore()
  978.  *             ------------
  979.  *             This command takes a list of filenames. Each filename listed here
  980.  *             is remembered, and when CHECKFILES is done, any filename listed
  981.  *             here will be marked as valid, regardless of whether it really IS
  982.  *             valid or not. This is useful if you have some files online which
  983.  *             are updated by external programs (such as, for example, the output
  984.  *             from BBSINFO), and hence have "wandering" file sizes.
  985.  */
  986. void com_ignore()
  987. {
  988.        IGNORE *ig;
  989.        char *p;
  990.  
  991.        while (compos < comlen) {
  992.                ig = mymalloc(sizeof(IGNORE));
  993.                p = getstring();
  994.                if (strlen(p) >= CAT_LEN) {
  995.                        scripterror("filename too long.\n");
  996.                        Cleanup(10);
  997.                }
  998.                strcpy(ig->name, p);
  999.                ig->next = firstignore;
  1000.                firstignore = ig;
  1001.        }
  1002. }
  1003.